﻿namespace dotnet_notes.Tools
{
    /// <summary>
    /// 图片工具类
    /// </summary>
    public static class ImageTool
    {
        private static readonly Random random = new();

        /// <summary>
        /// 检查图片后缀是否有效
        /// </summary>
        /// <param name="fileName">图片文件名</param>
        /// <returns></returns>
        public static bool CheckSuffix(string fileName)
        {
            string suffix = Path.GetExtension(fileName);
            return FileConfig.ImageSuffixArray.Contains(suffix);
        }

        /// <summary>
        /// 通过图片后缀获取对应的 Content-Type
        /// </summary>
        /// <param name="suffix">图片后缀</param>
        /// <returns></returns>
        public static string GetContentType(string suffix)
        {
            return suffix switch
            {
                ".jpg" => "image/jpeg",
                ".jpeg" => "image/jpeg",
                ".png" => "image/png",
                ".webp" => "image/webp",
                _ => throw new InvalidOperationException("不支持的图片后缀")
            };
        }

        /// <summary>
        /// 通过图片后缀获取 SkiaSharp 图片格式枚举值
        /// </summary>
        /// <param name="suffix">图片后缀</param>
        /// <returns></returns>
        public static SKEncodedImageFormat GetSKImageFormat(string suffix)
        {
            return suffix switch
            {
                ".jpg" => SKEncodedImageFormat.Jpeg,
                ".jpeg" => SKEncodedImageFormat.Jpeg,
                ".png" => SKEncodedImageFormat.Png,
                ".webp" => SKEncodedImageFormat.Webp,
                _ => throw new InvalidOperationException("不支持的图片后缀")
            };
        }

        /// <summary>
        /// 生成验证码图片
        /// </summary>
        /// <param name="content">验证码内容</param>
        /// <returns></returns>
        public static Stream GenerateValidateCode(string content)
        {
            int elementWidth = 32;
            int height = 44;

            using var bitmap = new SKBitmap(content.Length * elementWidth, height); // 指定图片宽高
            using var canvas = new SKCanvas(bitmap);

            canvas.Clear(SKColors.White); // 清空背景色

            using var typeface = SKTypeface.FromFamilyName
                (null, SKFontStyleWeight.ExtraBold, SKFontStyleWidth.Normal, SKFontStyleSlant.Italic);

            // 字体样式
            using var paint = new SKPaint
            {
                IsAntialias = true,
                Color = SKColors.Blue,
                TextSize = elementWidth,
                Typeface = typeface
            };

            var codeChars = content.Select((c, i) => new { Char = c.ToString(), Index = i });

            foreach (var item in codeChars)
            {
                var r = random.Next(100);
                var g = random.Next(100);
                var b = 255 - r - g;
                paint.Color = new SKColor((byte)r, (byte)g, (byte)b); // 随机颜色

                // 绘制验证码(x 和 y 有一个随机偏移)
                canvas.DrawText(
                    item.Char, 
                    item.Index * elementWidth + random.Next(5), 
                    elementWidth + random.Next(10), 
                    paint);
            }

            // 噪点
            for (int i = 0; i < 1000; i++)
            {
                int x = random.Next(bitmap.Width);
                int y = random.Next(bitmap.Height);
                bitmap.SetPixel(x, y, new SKColor((uint)random.Next()));
            }

            return bitmap.Encode(SKEncodedImageFormat.Jpeg, 75).AsStream();
        }

        /// <summary>
        /// 生成二维码图片
        /// </summary>
        /// <param name="content">二维码内容</param>
        /// <returns></returns>
        public static Stream GenerateQrCode(string content)
        {
            using var generator = new QRCodeGenerator();
            var qrCode = generator.CreateQrCode(content, ECCLevel.H); // 设置纠错能力为最高

            int width = 300;
            int height = 300;

            #region 方式一

            using var bitmap = new SKBitmap(width, height);
            using var canvas = new SKCanvas(bitmap);
            canvas.Render(qrCode, width, height);

            var fs = bitmap.Encode(SKEncodedImageFormat.Jpeg, 100).AsStream();

            #endregion

            #region 方式二

            //using var surface = SKSurface.Create(new SKImageInfo(width, height));
            //var canvas = surface.Canvas;
            //canvas.Render(qrCode, width, height); // 渲染二维码到画布

            //using var image = surface.Snapshot(); // 获取画布快照
            //var fs = image.Encode(SKEncodedImageFormat.Jpeg, 100).AsStream();

            #endregion

            return fs;
        }

        /// <summary>
        /// 保存图片为缩略图
        /// </summary>
        /// <param name="imageBytes">图片字节数据</param>
        /// <param name="basePath">保存的文件夹路径</param>
        /// <param name="imageName">保存的缩略图名称</param>
        public static void SaveAsThumbnail(byte[] imageBytes, string basePath, string imageName)
        {
            using var bitmap = SKBitmap.Decode(imageBytes);
            int width = bitmap.Width / 3; // 缩略图宽度
            int height = bitmap.Height / 3; // 缩略图高度
            int quality = 80; // 缩略图质量百分比

            Directory.CreateDirectory(basePath);

            string filePath = Path.Combine(basePath, imageName);
            using var fs = File.OpenWrite(filePath);
            string suffix = Path.GetExtension(imageName);

            // 调整图片大小
            using var thumbBitmap = bitmap.Resize(new SKImageInfo(width, height), SKFilterQuality.Medium);
            thumbBitmap.Encode(GetSKImageFormat(suffix), quality).SaveTo(fs);
        }

        /// <summary>
        /// 图片添加时间水印
        /// </summary>
        /// <param name="imageBytes">图片字节数据</param>
        /// <param name="basePath">保存的文件夹路径</param>
        /// <param name="imageName">保存的水印图片名称</param>
        public static void AddWatermarkWithTime(byte[] imageBytes, string basePath, string imageName)
        {
            using var bitmap = SKBitmap.Decode(imageBytes);
            int imgWidth = bitmap.Width;
            int imgHeight = bitmap.Height;
            int fontSize = (int)(imgWidth * 0.02); // 水印文字的字体大小
            byte alpha = 100; // 透明度 0 - 255

            // 字体
            using var typeface = SKTypeface.FromFamilyName(
                "SimSun",
                SKFontStyleWeight.Normal,
                SKFontStyleWidth.Normal,
                SKFontStyleSlant.Italic);

            // 画笔
            using var paint = new SKPaint
            {
                IsAntialias = true,
                Typeface = typeface,
                TextSize = fontSize
            };

            // 计算文本宽度
            string text = DateTime.Now.ToDateTimeString();
            var textBounds = new SKRect();
            paint.MeasureText(text, ref textBounds);

            using var canvas = new SKCanvas(bitmap);
            float x = imgWidth - textBounds.Width - imgWidth * 0.01f; // 文字不顶到右边界
            float y = imgHeight - textBounds.Height;

            paint.Color = new SKColor(56, 56, 56, alpha); // 阴影
            canvas.DrawText(text, x + 1, y + 1, paint);

            paint.Color = new SKColor(255, 255, 0, alpha); // 黄色文字
            canvas.DrawText(text, x, y, paint);

            Directory.CreateDirectory(basePath);

            string filePath = Path.Combine(basePath, imageName);
            using var fs = File.OpenWrite(filePath);
            string suffix = Path.GetExtension(imageName);

            bitmap.Encode(GetSKImageFormat(suffix), 100).SaveTo(fs);
        }

        /// <summary>
        /// 图片添加文本水印
        /// </summary>
        /// <param name="imageBytes">图片字节数据</param>
        /// <param name="basePath">保存的文件夹路径</param>
        /// <param name="imageName">保存的水印图片名称</param>
        /// <param name="text">水印文本</param>
        public static void AddWatermarkWithText
            (byte[] imageBytes, string basePath, string imageName, string text)
        {
            using var bitmap = SKBitmap.Decode(imageBytes);
            int imgWidth = bitmap.Width;
            int imgHeight = bitmap.Height;
            int fontSize = (int)(imgWidth * 0.02);
            byte alpha = 100;

            using var typeface = SKTypeface.FromFamilyName(
                "SimSun",
                SKFontStyleWeight.Normal,
                SKFontStyleWidth.Normal,
                SKFontStyleSlant.Italic);

            using var paint = new SKPaint
            {
                IsAntialias = true,
                Typeface = typeface,
                TextSize = fontSize
            };

            #region 生成水印文本

            var textBounds = new SKRect();
            paint.MeasureText(text, ref textBounds); // 计算文本在图片上的宽高

            var sb = new StringBuilder();
            int count = (int)(imgWidth * 1.75 / textBounds.Width);

            for (int i = count; i > 0; i--)
            {
                sb.Append(text + " "); // 拼接出水印文本
            }

            text = sb.ToString();

            #endregion

            using var canvas = new SKCanvas(bitmap);
            float shearX = 0.2f; // 向左倾斜(因为 y 轴倾斜使文字变窄了, 所以反方向倾斜还原一下)
            float shearY = -0.3f; // 向上倾斜

            canvas.Skew(shearX, shearY);

            float x = -(imgWidth * 0.5f); // 往前图片的一半开始画水印
            float y = imgHeight - textBounds.Height; // 从顶部往下画
            
            while (y > 0) // 循环绘制(多行水印文字)
            {
                paint.Color = new SKColor(56, 56, 56, alpha);
                canvas.DrawText(text, x + 1, y + 1, paint);
               
                paint.Color = new SKColor(255, 255, 0, alpha);
                canvas.DrawText(text, x, y, paint);

                y -= Math.Max(imgHeight * 0.25f, fontSize * 2); // 每行水印文字的间距
            }

            Directory.CreateDirectory(basePath);

            string filePath = Path.Combine(basePath, imageName);
            using var fs = File.OpenWrite(filePath);
            string suffix = Path.GetExtension(imageName);

            bitmap.Encode(GetSKImageFormat(suffix), 100).SaveTo(fs);
        }
    }
}
